#ifndef ROTATION_READER_H
#define ROTATION_READER_H

class croprotentry   
	{
	public:

		static Par_Seed_Harvest par_default_date_seeding_harvest;
		static int flag_par_default_date_seeding_harvest;
		static void read_Rotation_Reader_Par_Maps()
		 {
			string root="./par/";
			string suffix=".par";
			string name="Par_Default_Date_Seeding_Harvest";
			par_default_date_seeding_harvest.read_paras(root,name,suffix);
		}
		
		timestamp seeding_date,harvest_date;
		croprotentry()
		{
			if(flag_par_default_date_seeding_harvest==0){
				read_Rotation_Reader_Par_Maps();
				flag_par_default_date_seeding_harvest=1;
			}
		}
		int site_id;
		int crop_id;
		int crop_typ;
		int harvestyear; //harvestyear==0 if there is a valid harvestdate;else harvestyear!=0

		int add_years_until_first_timestamp_is_bigger(timestamp &date,timestamp &reference_date)
		{
			if (date<reference_date){
				if((date.get_mon()==2)&&(date.get_day()>28))
					date.set(date.get_year(),date.get_mon(),28.0);
				date.set(reference_date.get_year(),date.get_mon(),date.get_day());
			}
			if (date<reference_date){
				if((date.get_mon()==2)&&(date.get_day()>28))
					date.set(date.get_year(),date.get_mon(),28.0);
				date.set(reference_date.get_year()+1,date.get_mon(),date.get_day());
			}
			return 0;
		}
		int decrease_date_until_optimum_or_constrain(timestamp &date,timestamp &optimum_date,timestamp &constrain)
		{
			do{
				date.set_add(-1.0);
			}
			while((date>optimum_date)&&(date>constrain));
			date.set_add(1.0);
			return 0;
		}
		int read_crop_seeding_date_with_precursor(list2d *croprot,croprotentry last_crop, unsigned int row)
		{
			 int year;
			 int mon;
			 double day;
			 double testdiff;
			 timestamp date,optimum_date;
			 //Test  if row meets a point in the 2d List of croprot
			 if ((row>=croprot->nrows)||(row<0))err::err2("croprotentry::read_croprotentry:id out of range");

			 year=(int)croprot->var["year_seed"][row];
			 mon=(int)croprot->var["mon_seed"][row];
			 day=croprot->var["day_seed"][row];
			 crop_id=(int)croprot->var["crop_id"][row];
			 site_id=(int)croprot->var["site_id"][row];
			 crop_typ=(int)croprot->var["crop_typ"][row];
			 			
			 //test if precursor has a valid harvest_date
			 if(last_crop.harvestyear==0)	  //valid harvest date
			 {
					if((mon>0)&&(day>0))	  // Test if valid seeding date for actual crop
					{   //yes, it has
							date.set(year,mon,day);
							add_years_until_first_timestamp_is_bigger(date,last_crop.harvest_date);
					}
					else  
					{	//no valid seeding date
						 mon=  (int)par_default_date_seeding_harvest.get_val_by_index(crop_id,crop_typ,"max_seed_mon");
						 day=  par_default_date_seeding_harvest.get_val_by_index(crop_id,crop_typ,"max_seed_day");
						 date.set(year,mon,day);
						 add_years_until_first_timestamp_is_bigger(date,last_crop.harvest_date);
						 year=date.get_year();
						 mon=(int)par_default_date_seeding_harvest.get_val_by_index(crop_id,crop_typ,"mean_seed_mon");
						 day=  par_default_date_seeding_harvest.get_val_by_index(crop_id,crop_typ,"mean_seed_day");
						 optimum_date.set(year,mon,day);
						 decrease_date_until_optimum_or_constrain(date,optimum_date,last_crop.harvest_date);
					}
			 }
			 else{	//no valid harvest date from precursor crop
					if((mon>0)&&(day>0))	  // Test if valid seeding date for actual crop
					{   //yes, it has
							date.set(last_crop.harvestyear,mon,day);
							last_crop.harvest_date=date;
							last_crop.harvest_date.set_add(-10.0);
							testdiff=last_crop.seeding_date.get_dist(last_crop.harvest_date);
							if(testdiff<100)err::err3("croprotentry::read_croprotentry_with_precursor: quite small growing period for last years crop");
					}
					else  
					{	//no valid seeding date
						 mon=  (int)par_default_date_seeding_harvest.get_val_by_index(crop_id,crop_typ,"max_seed_mon");
						 day=  par_default_date_seeding_harvest.get_val_by_index(crop_id,crop_typ,"max_seed_day");
						 date.set(last_crop.harvestyear,mon,day);
						 last_crop.harvest_date=date;
						 last_crop.harvest_date.set_add(-10.0);
						 testdiff=last_crop.seeding_date.get_dist(last_crop.harvest_date);
						 if(testdiff<100)err::err2("croprotentry::read_croprotentry_with_precursor: quite small growing period for last years crop");
					}
			}
			seeding_date=date;
			return 0;
	}
			
 		int read_crop_seeding_date_without_precursor(list2d *croplist2D,unsigned int row)
		{
			// this function just reads a row (defined by int row) in the list2d except the harvest date
			 int year;
			 int mon;
			 double day;
			 timestamp date,optimum_date;
			 //Test  if row meets a point in the 2d List of croprot
			 if ((row>=croplist2D->nrows)||(row<0))err::err2("croprotentry::read_croprotentry:id out of range");

			 year=(int)croplist2D->var["year_seed"][row];
			 mon=(int)croplist2D->var["mon_seed"][row];
			 day=croplist2D->var["day_seed"][row];
			 crop_id=(int)croplist2D->var["crop_id"][row];
			 site_id=(int)croplist2D->var["site_id"][row];
			 crop_typ=(int)croplist2D->var["crop_typ"][row];
			 
			if((mon>0)&&(day>0))	  // Test if valid seeding date for actual crop
			{   //yes, it has
					date.set(year,mon,day);
			}
			else  
			{	//no valid seeding date
				 mon=  (int)par_default_date_seeding_harvest.get_val_by_index(crop_id,crop_typ,"mean_seed_mon");
				 day=  par_default_date_seeding_harvest.get_val_by_index(crop_id,crop_typ,"mean_seed_day");
				 date.set(year,mon,day);
			}
			seeding_date=date;
			return 0;
		}
		int read_crop_harvest_date(list2d *croprot,int row)
			{
				int year,mon;
				double day;
				int yeardiff;
				timestamp date;
				yeardiff=(int)croprot->var["year_harvest"][row]-(int)croprot->var["year_seed"][row];
				year=this->seeding_date.get_year()+yeardiff;
				mon=(int)croprot->var["mon_harvest"][row];
				day=croprot->var["day_harvest"][row];
				if((mon>0)&&(day>0)){		  // a valid harvest date
					date.set(year,mon,day);
					if(date< this->seeding_date)err::err2("croprotentry::read_crop_harvest_date: seeding date is bigger than harvest date");
					this->harvestyear=0;
					this->harvest_date=date;
					}
				else{		  //no valid harvest date
					if(this->crop_typ<3){
							mon=  (int)par_default_date_seeding_harvest.get_val_by_index(crop_id,crop_typ,"harvest_mon");
							day=  par_default_date_seeding_harvest.get_val_by_index(crop_id,crop_typ,"harvest_day");
							yeardiff=(int)croprot->var["year_harvest"][row]-(int)croprot->var["year_seed"][row];
							year=this->seeding_date.get_year()+yeardiff;
							date.set(year,mon,day);
							if(date< this->seeding_date)err::err2("croprotentry::read_crop_harvest_date: seeding date is bigger than harvest date");
							this->harvestyear=0;
							this->harvest_date=date;
						}
					else{
							yeardiff=(int)croprot->var["year_harvest"][row]-(int)croprot->var["year_seed"][row];
							this->harvestyear=this->seeding_date.get_year()+yeardiff;
						}
				}
				return 0;
		}
		croprotentry& operator=(const croprotentry &input)
			{
				this->seeding_date=input.seeding_date;
				this->harvest_date=input.harvest_date;
				this->site_id=input.site_id;
				this->crop_id=input.crop_id;
				this->crop_typ=input.crop_typ;
				this->harvestyear=input.harvestyear;
				return *this;
			}
} ;

class Rotation_Reader
{
public:
	static int read_croprot_from_croprot (list2d *croplist,tempmap <string,double> &croprot,timestamp firstdate,timestamp lastdate,string sitename)
	{
		// builds up a croprotation from entries in croprot file 
		// the croprot_timeseries starts with the one given in croplist
		// --> but if the time between first seeding date in croplist and firstdate is bigger than half a year
			// the last crop of rotation in croplist will be inserted at the start (if its no fallow)
			// and if its still more than half a year the second last crop will be inserted
		// Missing timeintervalls between crops will be filled with fallow
		// if the sumaltion_period (firstdate_lastdate) exceeds the values given in croprot the previous rotation will be attached.

		// 1. seeding date and harvest date are given
		// 2. only years are given (month and days are negative values)
		// 3. harvest date can be eiter given complete or not at all (for perennials)
		// 4. if perennial harvest is not given harvest will be assumed shortly (1 month) before next seeding
		int firstentry_pos,lastentry_pos,croprotcount,croprotcount_old=-1;
		int loopcounter=0;
		unsigned int row;
		string idstring;
		croprotentry entry1,last_crop;
	    croprot.set_clear();
		time_intervall timint;

		//search for first/last value
		firstentry_pos=croplist->get_pos_of_first_entry(sitename);
		lastentry_pos=croplist->get_pos_of_last_entry(sitename);
		if (firstentry_pos==croplist->nrows){
			err::err2("plot::read_croprot_from_croprot: no crop rotation given in Input");
			return 1;
		}
		if (firstentry_pos<0){
			err::err2("plot::read_croprot_from_croprot: no crop rotation given in Input");
			return 1;
		}
		if (lastentry_pos<0){
			err::err2("plot::read_croprot_from_croprot: no crop rotation given in Input");
			return 1;
		}
		
		croprotcount=firstentry_pos;
		do{
			if (loopcounter==0){
					entry1.read_crop_seeding_date_without_precursor(croplist,croprotcount);
				}
			else{
					entry1.read_crop_seeding_date_with_precursor(croplist, last_crop,croprotcount);
				}
			 entry1.read_crop_harvest_date(croplist,croprotcount);
			 if(croprotcount_old>=0){
				 timint.set(last_crop.seeding_date,last_crop.harvest_date);
				 croprot.point_time()->push_back(timint);
				
				 for(row=0;row<croplist->var_id.size();row++){
					 idstring=croplist->var_id[row];
					 if((idstring!="year_seed")&&(idstring!="mon_seed")&&(idstring!="day_seed")&&
						 (idstring!="year_harvest")&&(idstring!="mon_harvest")&&(idstring!="day_harvest")){
							 croprot.point_val(idstring)->push_back(croplist->var[idstring][croprotcount_old]);
					 }
				 }
				 
				//croprot.val["year_harvest"].push_back(last_crop.harvest_date.get_year());
				//croprot.val["mon_harvest"].push_back(last_crop.harvest_date.getmon());
				//croprot.val["day_harvest"].push_back(last_crop.harvest_date.getday());
			 }
			 last_crop=entry1;
			 croprotcount_old=croprotcount;
			 croprotcount=(croprotcount==lastentry_pos)*firstentry_pos+(croprotcount<lastentry_pos)*(croprotcount+1);
			//write parameters into the croprot tempmap
			 
			loopcounter++;
		}
		while(last_crop.harvest_date<lastdate);
		timint.set(last_crop.seeding_date,last_crop.harvest_date);
		croprot.point_time()->push_back(timint);
		 for(row=0;row<croplist->var_id.size();row++){
			 idstring=croplist->var_id[row];
			 if((idstring!="year_seed")&&(idstring!="mon_seed")&&(idstring!="day_seed")&&
						 (idstring!="year_harvest")&&(idstring!="mon_harvest")&&(idstring!="day_harvest")){
							 croprot.point_val(idstring)->push_back(croplist->var[idstring][croprotcount_old]);
			 }
		 }
		set_adjacent_croprotation(croplist,croprot,firstdate, lastdate);

		return 0;
	}
	static int set_adjacent_croprotation(list2d *croplist,tempmap <string,double> &croprot,timestamp firstdate,
		timestamp lastdate)
	{
		int pos;
		double diff_constraint=0;
		time_intervall testtime;
		//1 Check the time period between firstdate and first seeding in croprot
		double diff= firstdate.get_dist(croprot.get_start());
		if (diff<0) return 0;// first seeding in croprot is early enough
		if (diff<=diff_constraint) return 0; // first seeding in croprot is early enough
		pos=croprot.get_size()-1;
		do{
			testtime=croprot.get_time_intervall(pos);
			
			while(testtime>=croprot.get_time_intervall(0)){
				testtime.set_move_year(-1);
			}
			
			if(croprot.set_insert_a_row_entry(0,croprot,pos,testtime)) 
				err::err3("read_croprotations::set_adjacent_croprotation:could not insert a element");
			croprot.set_time_intervall(0,testtime);
			diff=firstdate.get_dist(croprot.get_start());
		}
		while (diff>diff_constraint);


		return 0;
	}
	static time_intervall get_time_intervall_of_growth (int crop_id,timestamp firstdate,timestamp lastdate,int &croptype)
	{
		// This function returns a timeintervall  "growthtime" of crop growth taken from default values of a parameter map
		// Input is the crop_id, and an enveloping predifined intervall by timestamp firstdate and timestamp lastdate and a croptype
		// Intercrops are not considered in the case if intercrop growthtime=enveloping time intervall
		// if the crop is a perrenial then the growthime becomes the enveloping time intervall 
		// If the crop is a wintercrop then there are two cases
		//	-->First: firstdate.mon > default.harvest.mon than year of seed is year of firstdate
		//	-->Second: firstdate.mon < default harvest than year of seed is firstdate year-1
		// If the crop is a summer crop then there are two cases:
		//	--> firstdate.mon > default_harvest_mon then year of sseed=firstdate. year+1
		//	--> firstdate.mon < default_harvest_mon then year of seed =firstdate.year

		int year_seed,mon_seed;
		int year_harvest,mon_harvest;
		double day_seed,day_harvest;
		timestamp time_seed,time_harvest;
		time_intervall growthtime;
		if(croprotentry::par_default_date_seeding_harvest.get_val_by_index(crop_id,0,"mean_seed_mon")>0){
			mon_seed=(int)croprotentry::par_default_date_seeding_harvest.get_val_by_index(crop_id,0,"mean_seed_mon");
			day_seed=croprotentry::par_default_date_seeding_harvest.get_val_by_index(crop_id,0,"mean_seed_day");
			mon_harvest=(int)croprotentry::par_default_date_seeding_harvest.get_val_by_index(crop_id,0,"harvest_mon");
			day_harvest=croprotentry::par_default_date_seeding_harvest.get_val_by_index(crop_id,0,"harvest_day");
			if(firstdate.get_mon()>=mon_harvest){
				year_seed=firstdate.get_year();year_harvest=year_seed+1;}
			else{ year_seed=firstdate.get_year()-1;year_harvest=year_seed+1;}
			time_seed.set(year_seed,mon_seed,day_seed);
			time_harvest.set(year_harvest,mon_harvest,day_harvest);
			growthtime.set(time_seed,time_harvest);
			croptype=0;
		}
		else if(croprotentry::par_default_date_seeding_harvest.get_val_by_index(crop_id,1,"mean_seed_mon")>0){
			mon_seed=(int)croprotentry::par_default_date_seeding_harvest.get_val_by_index(crop_id,1,"mean_seed_mon");
			day_seed=croprotentry::par_default_date_seeding_harvest.get_val_by_index(crop_id,1,"mean_seed_day");
			mon_harvest=(int)croprotentry::par_default_date_seeding_harvest.get_val_by_index(crop_id,1,"harvest_mon");
			day_harvest=croprotentry::par_default_date_seeding_harvest.get_val_by_index(crop_id,1,"harvest_day");
			if(firstdate.get_mon()>=mon_harvest){
				year_seed=firstdate.get_year()+1;year_harvest=year_seed;}
			else{ year_seed=firstdate.get_year();year_harvest=year_seed;}
			time_seed.set(year_seed,mon_seed,day_seed);
			time_harvest.set(year_harvest,mon_harvest,day_harvest);
			growthtime.set(time_seed,time_harvest);
			croptype=1;
		}
		else {
			growthtime.set(firstdate,lastdate);
			croptype=3;
		}
		return growthtime;
			
	}
};
#endif
